// @ts-nocheck
import React from 'react';
import {
    createRendererEvent,
    OnEventProps,
    Renderer,
    RendererProps,
    runActions,
    CustomStyle,
    autobind, createObject,ScopedContext, IScopedContext,BaseSchemaWithoutType,isApiOutdated,resolveVariableAndFilter
} from 'amis-core';
import {ActionSchema} from "amis";

/**
 * IFrame Plus 渲染器
 * 文档：https://aisuda.bce.baidu.com/amis/zh-CN/components/iframe
 */
type SchemaTokenizeableString = string;
type SchemaUrlPath = SchemaTokenizeableString;
interface BaseSchema extends BaseSchemaWithoutType {
    type: string;
}
export interface IFramePlusSchema extends BaseSchema {
    type: 'iframe-plus';

    /**
     * 页面地址
     */
    src: SchemaUrlPath;

    /**
     * 事件相应，配置后当 iframe 通过 postMessage 发送事件时，可以触发 AMIS 内部的动作。
     */
    events?: {
        [eventName: string]: ActionSchema;
    };

    // 事件动作
    onEvent?: OnEventProps['onEvent'];

    width?: number | string;
    height?: number | string;

    allow?: string;

    name?: string;

    referrerpolicy?:
        | 'no-referrer'
        | 'no-referrer-when-downgrade'
        | 'origin'
        | 'origin-when-cross-origin'
        | 'same-origin'
        | 'strict-origin'
        | 'strict-origin-when-cross-origin'
        | 'unsafe-url';

    sandbox?: string;
}

export interface IFramePlusProps
    extends RendererProps,
        Omit<IFramePlusSchema, 'type' | 'className'> {}

export default class IFramePlus extends React.Component<IFramePlusProps, object> {
    IFrameRef: React.RefObject<HTMLIFrameElement> = React.createRef();
    static propsList: Array<string> = ['src', 'className'];
    static defaultProps: Partial<IFramePlusProps> = {
        className: '',
        frameBorder: 0
    };

    state = {
        width: this.props.width || '100%',
        height: this.props.height || '100%'
    };

    componentDidMount() {
        window.addEventListener('message', this.onMessage);
    }

    componentDidUpdate(prevProps: IFramePlusProps) {
        const data = this.props.data;
        console.log('------componentDidUpdate------', data)
        if (data !== prevProps.data) {
            this.postMessage('update', data);
        } else if (
            this.props.width !== prevProps.width ||
            this.props.height !== prevProps.height
        ) {
            this.setState({
                width: this.props.width || '100%',
                height: this.props.height || '100%'
            });
        }
    }

    componentWillUnmount() {
        window.removeEventListener('message', this.onMessage);
    }

    /** 校验URL是否合法 */
    validateURL(url: any) {
        // base64编码格式
        if (
            url &&
            typeof url === 'string' &&
            /^data:([a-zA-Z0-9]+\/[a-zA-Z0-9]+);base64,.*/.test(url)
        ) {
            return true;
        }

        // HTTP[S]协议
        if (
            url &&
            typeof url === 'string' &&
            !/^(\.\/|\.\.\/|\/|https?\:\/\/|\/\/)/.test(url)
        ) {
            return false;
        }

        return true;
    }

    @autobind
    async onMessage(e: MessageEvent) {
        const {events, onEvent, onAction, data} = this.props;
        const postData = JSON.parse(e.data);
        console.log('------onMessage------', e, postData)
        if (typeof postData?.type !== 'string') {
            return;
        }

        const type = postData.type;

        if (!type) {
            return;
        }
        const eventConfig: any = onEvent?.[type];

        if (eventConfig && eventConfig.actions?.length) {
            const rendererEvent = createRendererEvent(type, {
                env: this.props?.env,
                nativeEvent: e,
                data: createObject(data, postData),
                scoped: this.context
            });
            await runActions(eventConfig.actions, this, rendererEvent);

            if (rendererEvent.prevented) {
                return;
            }
        }

        if (events) {
            const action = events[type];
            action && onAction(e, action, createObject(data, postData));
        }

    }

    @autobind
    onLoad() {
        const {src, data,defaultData} = this.props;
        console.log('------onLoad------', this.props)
        src && this.postMessage('init', data);
    }

    // 当别的组件通知 iframe reload 的时候执行。
    @autobind
    reload(subpath?: any, query?: any) {
        if (query) {
            return this.receive(query);
        }

        const {src, data} = this.props;

        if (src) {
            (this.IFrameRef.current as HTMLIFrameElement).src =
                resolveVariableAndFilter(src, data, '| raw');
        }
    }

    // 当别的组件把数据发给 iframe 里面的时候执行。
    @autobind
    receive(values: object) {
        const {src, data} = this.props;
        const newData = createObject(data, values);

        this.postMessage('receive', newData);

        if (isApiOutdated(src, src, data, newData)) {
            (this.IFrameRef.current as HTMLIFrameElement).src =
                resolveVariableAndFilter(src, newData, '| raw');
        }
    }
    @autobind
    postMessage(type: string, data: any) {
        // 发送消息到IFrame 不对type的限制
        (this.IFrameRef.current as HTMLIFrameElement)?.contentWindow?.postMessage(
            JSON.stringify(data),
            '*'
        );
    }

    render() {
        const {width, height} = this.state;
        let {
            className,
            src,
            name,
            frameBorder,
            data,
            style,
            allow,
            sandbox,
            referrerpolicy,
            translate: __,
            id,
            wrapperCustomStyle,
            env,
            themeCss,
            baseControlClassName,
            classnames: cx
        } = this.props;

        let tempStyle: any = {};

        width !== void 0 && (tempStyle.width = width);
        height !== void 0 && (tempStyle.height = height);

        style = {
            ...tempStyle,
            ...style
        };

        const finalSrc = src
            ? resolveVariableAndFilter(src, data, '| raw')
            : undefined;

        if (!this.validateURL(finalSrc)) {
            return <p>{__('Iframe.invalid')}</p>;
        }

        if (
            location.protocol === 'https:' &&
            finalSrc &&
            finalSrc.startsWith('http://')
        ) {
            env.notify('error', __('Iframe.invalidProtocol'));
        }

      return (
            <>
                <iframe
                    name={name}
                    className={cx(
                        'IFrame',
                        className,
                        baseControlClassName,
                        wrapperCustomStyle
                            ? ` wrapperCustomStyle-${id?.replace('u:', '')}`
                            : ''
                    )}
                    frameBorder={frameBorder}
                    style={style}
                    ref={this.IFrameRef}
                    onLoad={this.onLoad}
                    src={finalSrc}
                    allow={allow}
                    referrerPolicy={referrerpolicy}
                    sandbox={sandbox}
                />
                <CustomStyle
                    config={{
                        id,
                        themeCss,
                        classNames: [
                            {
                                key: 'baseControlClassName',
                                // @ts-ignore
                                value: baseControlClassName
                            }
                        ]
                    }}
                    env={env}
                />
            </>
        );
    }
}
@Renderer({
    type: 'iframe-plus'
})
export class IFramePlusRenderer extends IFramePlus {
    static contextType = ScopedContext;

    constructor(props: IFramePlusProps, context: IScopedContext) {
        super(props);

        const scoped = context;
        scoped.registerComponent(this);
    }

    componentWillUnmount() {
        const scoped = this.context as IScopedContext;
        scoped.unRegisterComponent(this);
    }
}
